4 using UnityEngine
.Tilemaps
;
5 using Object
= UnityEngine
.Object
;
9 [CustomGridBrush(true, false, false, "GameObject Brush")]
10 public class GameObjectBrush
: GridBrushBase
14 private BrushCell
[] m_Cells
;
18 private Vector3Int m_Size
;
22 private Vector3Int m_Pivot
;
24 public Vector3Int size { get { return m_Size; }
set { m_Size = value; SizeUpdated(); }
}
25 public Vector3Int pivot { get { return m_Pivot; }
set { m_Pivot = value; }
}
26 public BrushCell
[] cells { get { return m_Cells; }
}
27 public int cellCount { get { return m_Cells != null ? m_Cells.Length : 0; }
}
29 public GameObjectBrush()
31 Init(Vector3Int
.one
, Vector3Int
.zero
);
35 public void Init(Vector3Int size
)
37 Init(size
, Vector3Int
.zero
);
41 public void Init(Vector3Int size
, Vector3Int pivot
)
48 public override void Paint(GridLayout gridLayout
, GameObject brushTarget
, Vector3Int position
)
50 // Do not allow editing palettes
51 if (brushTarget
.layer
== 31)
54 Vector3Int min
= position
- pivot
;
55 BoundsInt bounds
= new BoundsInt(min
, m_Size
);
56 BoxFill(gridLayout
, brushTarget
, bounds
);
59 private void PaintCell(GridLayout grid
, Vector3Int position
, Transform parent
, BrushCell cell
)
61 if (cell
.gameObject
!= null)
63 SetSceneCell(grid
, parent
, position
, cell
.gameObject
, cell
.offset
, cell
.scale
, cell
.orientation
);
67 public override void Erase(GridLayout gridLayout
, GameObject brushTarget
, Vector3Int position
)
69 // Do not allow editing palettes
70 if (brushTarget
.layer
== 31)
73 Vector3Int min
= position
- pivot
;
74 BoundsInt bounds
= new BoundsInt(min
, m_Size
);
75 BoxErase(gridLayout
, brushTarget
, bounds
);
78 private void EraseCell(GridLayout grid
, Vector3Int position
, Transform parent
)
80 ClearSceneCell(grid
, parent
, position
);
83 public override void BoxFill(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
)
85 // Do not allow editing palettes
86 if (brushTarget
.layer
== 31)
89 if (brushTarget
== null)
92 foreach (Vector3Int location
in position
.allPositionsWithin
)
94 Vector3Int local
= location
- position
.min
;
95 BrushCell cell
= m_Cells
[GetCellIndexWrapAround(local
.x
, local
.y
, local
.z
)];
96 PaintCell(gridLayout
, location
, brushTarget
.transform
, cell
);
100 public override void BoxErase(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
)
102 // Do not allow editing palettes
103 if (brushTarget
.layer
== 31)
106 if (brushTarget
== null)
109 foreach (Vector3Int location
in position
.allPositionsWithin
)
111 EraseCell(gridLayout
, location
, brushTarget
.transform
);
115 public override void FloodFill(GridLayout gridLayout
, GameObject brushTarget
, Vector3Int position
)
117 Debug
.LogWarning("FloodFill not supported");
120 public override void Rotate(RotationDirection direction
, Grid
.CellLayout layout
)
122 Vector3Int oldSize
= m_Size
;
123 BrushCell
[] oldCells
= m_Cells
.Clone() as BrushCell
[];
124 size
= new Vector3Int(oldSize
.y
, oldSize
.x
, oldSize
.z
);
125 BoundsInt oldBounds
= new BoundsInt(Vector3Int
.zero
, oldSize
);
127 foreach (Vector3Int oldPos
in oldBounds
.allPositionsWithin
)
129 int newX
= direction
== RotationDirection
.Clockwise
? oldSize
.y
- oldPos
.y
- 1 : oldPos
.y
;
130 int newY
= direction
== RotationDirection
.Clockwise
? oldPos
.x
: oldSize
.x
- oldPos
.x
- 1;
131 int toIndex
= GetCellIndex(newX
, newY
, oldPos
.z
);
132 int fromIndex
= GetCellIndex(oldPos
.x
, oldPos
.y
, oldPos
.z
, oldSize
.x
, oldSize
.y
, oldSize
.z
);
133 m_Cells
[toIndex
] = oldCells
[fromIndex
];
136 int newPivotX
= direction
== RotationDirection
.Clockwise
? oldSize
.y
- pivot
.y
- 1 : pivot
.y
;
137 int newPivotY
= direction
== RotationDirection
.Clockwise
? pivot
.x
: oldSize
.x
- pivot
.x
- 1;
138 pivot
= new Vector3Int(newPivotX
, newPivotY
, pivot
.z
);
140 Matrix4x4 rotation
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.Euler(0f
, 0f
, direction
== RotationDirection
.Clockwise
? 90f
: -90f
), Vector3
.one
);
141 Quaternion orientation
= Quaternion
.Euler(0f
, 0f
, direction
== RotationDirection
.Clockwise
? 90f
: -90f
);
142 foreach (BrushCell cell
in m_Cells
)
144 cell
.offset
= rotation
* cell
.offset
;
145 cell
.orientation
= cell
.orientation
* orientation
;
149 public override void Flip(FlipAxis flip
, Grid
.CellLayout layout
)
151 if (flip
== FlipAxis
.X
)
157 public override void Pick(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
, Vector3Int pickStart
)
159 // Do not allow editing palettes
160 if (brushTarget
.layer
== 31)
164 UpdateSizeAndPivot(new Vector3Int(position
.size
.x
, position
.size
.y
, 1), new Vector3Int(pickStart
.x
, pickStart
.y
, 0));
166 foreach (Vector3Int pos
in position
.allPositionsWithin
)
168 Vector3Int brushPosition
= new Vector3Int(pos
.x
- position
.x
, pos
.y
- position
.y
, 0);
169 PickCell(pos
, brushPosition
, gridLayout
, brushTarget
.transform
);
173 private void PickCell(Vector3Int position
, Vector3Int brushPosition
, GridLayout grid
, Transform parent
)
177 Vector3 cellCenter
= grid
.LocalToWorld(grid
.CellToLocalInterpolated(position
+ new Vector3(.5f
, .5f
, .5f
)));
178 GameObject go
= GetObjectInCell(grid
, parent
, position
);
182 Object prefab
= PrefabUtility
.GetCorrespondingObjectFromSource(go
);
186 SetGameObject(brushPosition
, (GameObject
) prefab
);
190 GameObject newInstance
= Instantiate(go
);
191 newInstance
.hideFlags
= HideFlags
.HideAndDontSave
;
192 SetGameObject(brushPosition
, newInstance
);
195 SetOffset(brushPosition
, go
.transform
.position
- cellCenter
);
196 SetScale(brushPosition
, go
.transform
.localScale
);
197 SetOrientation(brushPosition
, go
.transform
.localRotation
);
202 public override void MoveStart(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
)
204 // Do not allow editing palettes
205 if (brushTarget
.layer
== 31)
209 UpdateSizeAndPivot(new Vector3Int(position
.size
.x
, position
.size
.y
, 1), Vector3Int
.zero
);
211 if (brushTarget
!= null)
213 foreach (Vector3Int pos
in position
.allPositionsWithin
)
215 Vector3Int brushPosition
= new Vector3Int(pos
.x
- position
.x
, pos
.y
- position
.y
, 0);
216 PickCell(pos
, brushPosition
, gridLayout
, brushTarget
.transform
);
217 ClearSceneCell(gridLayout
, brushTarget
.transform
, brushPosition
);
222 public override void MoveEnd(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
)
224 // Do not allow editing palettes
225 if (brushTarget
.layer
== 31)
228 Paint(gridLayout
, brushTarget
, position
.min
);
234 foreach (var cell
in m_Cells
)
236 if (cell
.gameObject
!= null && !EditorUtility
.IsPersistent(cell
.gameObject
))
238 DestroyImmediate(cell
.gameObject
);
241 UpdateSizeAndPivot(Vector3Int
.one
, Vector3Int
.zero
);
246 BrushCell
[] oldCells
= m_Cells
.Clone() as BrushCell
[];
247 BoundsInt oldBounds
= new BoundsInt(Vector3Int
.zero
, m_Size
);
249 foreach (Vector3Int oldPos
in oldBounds
.allPositionsWithin
)
251 int newX
= m_Size
.x
- oldPos
.x
- 1;
252 int toIndex
= GetCellIndex(newX
, oldPos
.y
, oldPos
.z
);
253 int fromIndex
= GetCellIndex(oldPos
);
254 m_Cells
[toIndex
] = oldCells
[fromIndex
];
257 int newPivotX
= m_Size
.x
- pivot
.x
- 1;
258 pivot
= new Vector3Int(newPivotX
, pivot
.y
, pivot
.z
);
259 Matrix4x4 flip
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(-1f
, 1f
, 1f
));
260 Quaternion orientation
= Quaternion
.Euler(0f
, 0f
, -180f
);
262 foreach (BrushCell cell
in m_Cells
)
264 Vector3 oldOffset
= cell
.offset
;
265 cell
.offset
= flip
* oldOffset
;
266 cell
.orientation
= cell
.orientation
*orientation
;
272 BrushCell
[] oldCells
= m_Cells
.Clone() as BrushCell
[];
273 BoundsInt oldBounds
= new BoundsInt(Vector3Int
.zero
, m_Size
);
275 foreach (Vector3Int oldPos
in oldBounds
.allPositionsWithin
)
277 int newY
= m_Size
.y
- oldPos
.y
- 1;
278 int toIndex
= GetCellIndex(oldPos
.x
, newY
, oldPos
.z
);
279 int fromIndex
= GetCellIndex(oldPos
);
280 m_Cells
[toIndex
] = oldCells
[fromIndex
];
283 int newPivotY
= m_Size
.y
- pivot
.y
- 1;
284 pivot
= new Vector3Int(pivot
.x
, newPivotY
, pivot
.z
);
285 Matrix4x4 flip
= Matrix4x4
.TRS(Vector3
.zero
, Quaternion
.identity
, new Vector3(1f
, -1f
, 1f
));
286 Quaternion orientation
= Quaternion
.Euler(0f
, 0f
, -180f
);
287 foreach (BrushCell cell
in m_Cells
)
289 Vector3 oldOffset
= cell
.offset
;
290 cell
.offset
= flip
* oldOffset
;
291 cell
.orientation
= cell
.orientation
* orientation
;
295 public void UpdateSizeAndPivot(Vector3Int size
, Vector3Int pivot
)
302 public void SetGameObject(Vector3Int position
, GameObject go
)
304 if (ValidateCellPosition(position
))
305 m_Cells
[GetCellIndex(position
)].gameObject
= go
;
308 public void SetOffset(Vector3Int position
, Vector3 offset
)
310 if (ValidateCellPosition(position
))
311 m_Cells
[GetCellIndex(position
)].offset
= offset
;
314 public void SetOrientation(Vector3Int position
, Quaternion orientation
)
316 if (ValidateCellPosition(position
))
317 m_Cells
[GetCellIndex(position
)].orientation
= orientation
;
320 public void SetScale(Vector3Int position
, Vector3 scale
)
322 if (ValidateCellPosition(position
))
323 m_Cells
[GetCellIndex(position
)].scale
= scale
;
326 public int GetCellIndex(Vector3Int brushPosition
)
328 return GetCellIndex(brushPosition
.x
, brushPosition
.y
, brushPosition
.z
);
331 public int GetCellIndex(int x
, int y
, int z
)
333 return x
+ m_Size
.x
* y
+ m_Size
.x
* m_Size
.y
* z
;
336 public int GetCellIndex(int x
, int y
, int z
, int sizex
, int sizey
, int sizez
)
338 return x
+ sizex
* y
+ sizex
* sizey
* z
;
341 public int GetCellIndexWrapAround(int x
, int y
, int z
)
343 return (x
% m_Size
.x
) + m_Size
.x
* (y
% m_Size
.y
) + m_Size
.x
* m_Size
.y
* (z
% m_Size
.z
);
346 private static GameObject
GetObjectInCell(GridLayout grid
, Transform parent
, Vector3Int position
)
348 int childCount
= parent
.childCount
;
349 Vector3 min
= grid
.LocalToWorld(grid
.CellToLocalInterpolated(position
));
350 Vector3 max
= grid
.LocalToWorld(grid
.CellToLocalInterpolated(position
+ Vector3Int
.one
));
352 // Infinite bounds on Z for 2D convenience
353 min
= new Vector3(min
.x
, min
.y
, float.MinValue
);
354 max
= new Vector3(max
.x
, max
.y
, float.MaxValue
);
356 Bounds bounds
= new Bounds((max
+ min
) * .5f
, max
- min
);
358 for (int i
= 0; i
< childCount
; i
++)
360 Transform child
= parent
.GetChild(i
);
361 if (bounds
.Contains(child
.position
))
362 return child
.gameObject
;
367 private bool ValidateCellPosition(Vector3Int position
)
370 position
.x
>= 0 && position
.x
< size
.x
&&
371 position
.y
>= 0 && position
.y
< size
.y
&&
372 position
.z
>= 0 && position
.z
< size
.z
;
374 throw new ArgumentException(string.Format("Position {0} is an invalid cell position. Valid range is between [{1}, {2}).", position
, Vector3Int
.zero
, size
));
378 private void SizeUpdated()
380 m_Cells
= new BrushCell
[m_Size
.x
* m_Size
.y
* m_Size
.z
];
381 BoundsInt bounds
= new BoundsInt(Vector3Int
.zero
, m_Size
);
382 foreach (Vector3Int pos
in bounds
.allPositionsWithin
)
384 m_Cells
[GetCellIndex(pos
)] = new BrushCell();
388 private static void SetSceneCell(GridLayout grid
, Transform parent
, Vector3Int position
, GameObject go
, Vector3 offset
, Vector3 scale
, Quaternion orientation
)
390 if (parent
== null || go
== null)
393 GameObject instance
= null;
394 if (PrefabUtility
.GetPrefabType(go
) == PrefabType
.Prefab
)
396 instance
= (GameObject
) PrefabUtility
.InstantiatePrefab(go
);
400 instance
= Instantiate(go
);
401 instance
.hideFlags
= HideFlags
.None
;
402 instance
.name
= go
.name
;
405 Undo
.RegisterCreatedObjectUndo(instance
, "Paint GameObject");
406 instance
.transform
.SetParent(parent
);
407 instance
.transform
.position
= grid
.LocalToWorld(grid
.CellToLocalInterpolated(new Vector3Int(position
.x
, position
.y
, position
.z
) + new Vector3(.5f
, .5f
, .5f
)));
408 instance
.transform
.localRotation
= orientation
;
409 instance
.transform
.localScale
= scale
;
410 instance
.transform
.Translate(offset
);
413 private static void ClearSceneCell(GridLayout grid
, Transform parent
, Vector3Int position
)
418 GameObject erased
= GetObjectInCell(grid
, parent
, new Vector3Int(position
.x
, position
.y
, position
.z
));
420 Undo
.DestroyObjectImmediate(erased
);
423 public override int GetHashCode()
428 foreach (var cell
in cells
)
430 hash
= hash
* 33 + cell
.GetHashCode();
437 public class BrushCell
439 public GameObject gameObject { get { return m_GameObject; }
set { m_GameObject = value; }
}
440 public Vector3 offset { get { return m_Offset; }
set { m_Offset = value; }
}
441 public Vector3 scale { get { return m_Scale; }
set { m_Scale = value; }
}
442 public Quaternion orientation { get { return m_Orientation; }
set { m_Orientation = value; }
}
445 private GameObject m_GameObject
;
447 Vector3 m_Offset
= Vector3
.zero
;
449 Vector3 m_Scale
= Vector3
.one
;
451 Quaternion m_Orientation
= Quaternion
.identity
;
453 public override int GetHashCode()
458 hash
= gameObject
!= null ? gameObject
.GetInstanceID() : 0;
459 hash
= hash
* 33 + m_Offset
.GetHashCode();
460 hash
= hash
* 33 + m_Scale
.GetHashCode();
461 hash
= hash
* 33 + m_Orientation
.GetHashCode();
468 [CustomEditor(typeof(GameObjectBrush
))]
469 public class GameObjectBrushEditor
: GridBrushEditorBase
471 public GameObjectBrush brush { get { return target as GameObjectBrush; }
}
473 public override void OnPaintSceneGUI(GridLayout gridLayout
, GameObject brushTarget
, BoundsInt position
, GridBrushBase
.Tool tool
, bool executing
)
475 BoundsInt gizmoRect
= position
;
477 if (tool
== GridBrushBase
.Tool
.Paint
|| tool
== GridBrushBase
.Tool
.Erase
)
478 gizmoRect
= new BoundsInt(position
.min
- brush
.pivot
, brush
.size
);
480 base.OnPaintSceneGUI(gridLayout
, brushTarget
, gizmoRect
, tool
, executing
);
483 public override void OnPaintInspectorGUI()
485 GUILayout
.Label("Pick, paint and erase GameObject(s) in the scene.");
486 GUILayout
.Label("Limited to children of the currently selected GameObject.");